<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>青云教育系统 - 教师端</title>
    <link rel="stylesheet" href="../../teacher.css">
    <script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.3/build/qrcode.min.js"></script>
</head>
<body>
    <div class="layout">
        <aside class="sidebar">
            <div class="brand">青云教育 · 教师端</div>
            <nav class="menu" id="menu">
                <button class="menu-item" data-view="profile" data-url="/index">教员信息及考勤</button>
                <button class="menu-item" data-view="schedule" data-url="/schedule">教师课表</button>
                <button class="menu-item" data-view="password" data-url="/password">修改密码</button>
                <button class="menu-item" data-view="students" data-url="/students">学生信息</button>
                <button class="menu-item" data-view="upload" data-url="/postmaterial">上传资料</button>
                <button class="menu-item" data-view="download" data-url="/downloadmaterial">下载资料</button>
                <button class="menu-item" data-view="mymaterials" data-url="/mymaterials">我的资料</button>
                
                
            </nav>
        </aside>
        <main class="content">
            <header class="topbar">
                <div class="left">
                    <h1 id="view-title">课堂反馈</h1>
                </div>
                <div class="right">
                    <div class="user-info">
                        <img id="user-avatar" class="user-avatar" src="" alt="用户头像">
                        <span id="teacher-name" class="user-name"></span>
                    </div>
                    <button id="logoutBtn" class="btn btn-secondary">退出登录</button>
                </div>
            </header>

            <section id="view-feedback" class="view" style="display:block;">
                <div class="card">
                    <div class="card-header">已提交的课堂反馈</div>
                    <div class="card-body">
                        <div class="feedback-table-wrapper">
                            <table class="feedback-table" id="feedbackTable">
                                <thead>
                                    <tr>
                                        <th>课程名称</th>
                                        <th>上课时间</th>
                                        <th>提交时间</th>
                                        <th>操作</th>
                                    </tr>
                                </thead>
                                <tbody id="feedbackList">
                                </tbody>
                            </table>
                        </div>
                        <div id="feedbackListEmpty" class="empty" style="display:none;">暂无该学生的课堂反馈</div>
                    </div>
                </div>

                <div class="card" style="margin-top: 16px;">
                    <div class="card-header">提交课堂反馈</div>
                    <div class="card-body">
                        <form id="feedbackForm" class="form">
                            <div class="form-group">
                                <label>当前学生</label>
                                <div id="currentStudent" class="tag">加载中...</div>
                            </div>
                            <div class="form-group">
                                <label>绑定课程（可选）</label>
                                <div class="inline">
                                    <input type="date" id="scheduleDate" style="max-width: 180px;">
                                    <select id="scheduleSelect" style="min-width: 260px;">
                                        <option value="">请选择当天课程（可选）</option>
                                    </select>
                                    <button type="button" class="btn btn-secondary" id="loadSchedulesBtn">加载课程</button>
                                </div>
                                <small>可从课表选择某节课进行绑定；若无合适，留空。</small>
                            </div>
                            <div class="form-group">
                                <label>课程/班级（自由文本，备选）</label>
                                <input type="text" id="courseText" placeholder="如：高二(3)班 数学">
                            </div>
                            <div class="form-group">
                                <label>反馈内容 <span class="required">*</span></label>
                                <textarea id="feedbackContent" rows="5" placeholder="请填写课堂反馈" required></textarea>
                            </div>
                            <div class="form-group">
                                <label>上传图片（最多9张，支持JPG/PNG）</label>
                                <input type="file" id="photoInput" accept="image/*" multiple>
                                <div id="photoPreview" class="gallery" style="margin-top:8px;"></div>
                                <small>可拖拽调整顺序；点击缩略图右上角×可移除。</small>
                            </div>
                            <div class="form-actions">
                                <button type="submit" class="btn btn-primary">提交反馈</button>
                                <button type="reset" class="btn btn-secondary">重置</button>
                            </div>
                            <div id="feedbackMsg" class="message" style="display:none;"></div>
                        </form>
                    </div>
                </div>

                
            </section>
        </main>
    </div>

    <script src="../../teacher.js"></script>
    <script>
        const API_BASE = 'http://localhost:8081';
        const MAX_PHOTOS = 9;
        let uploadedPhotos = []; // {url, sortNo}
        let currentPage = 1;
        let totalPages = 1;
        let currentStudentId = null;

        // 保障工具函数存在
        if (typeof getQueryParam !== 'function') {
            function getQueryParam(key) {
                try{
                    const url = new URL(window.location.href);
                    return url.searchParams.get(key);
                }catch(e){
                    const query = window.location.search.substring(1);
                    const pairs = query.split('&');
                    for (const p of pairs) {
                        const [k, v] = p.split('=');
                        if (decodeURIComponent(k || '') === key) return decodeURIComponent(v || '');
                    }
                    return null;
                }
            }
        }

        document.addEventListener('DOMContentLoaded', function(){
            // 退出登录按钮
            const logoutBtn = document.getElementById('logoutBtn');
            if (logoutBtn && !logoutBtn.hasAttribute('data-bound')) {
                logoutBtn.setAttribute('data-bound', 'true');
                logoutBtn.addEventListener('click', function(e) {
                    e.preventDefault();
                    if (typeof logout === 'function') logout();
                });
            }

            // 菜单跳转
            const menu = document.getElementById('menu');
            menu.addEventListener('click', function(e){
                const btn = e.target.closest('.menu-item');
                if(!btn) return;
                const view = btn.getAttribute('data-view');
                if(view === 'schedule') window.location.href = '/teacher/schedule/';
                else if(view === 'password') window.location.href = '/teacher/password/';
                else if(view === 'profile') window.location.href = '/index.html';
                else if(view === 'students') window.location.href = '/teacher/students/';
                else if(view === 'upload') window.location.href = '/teacher/postmaterial';
                else if(view === 'download') window.location.href = '/teacher/downloadmaterial/';
                else if(view === 'mymaterials') window.location.href = '/teacher/mymaterials/';
                else if(view === 'myfeedback') window.location.href = '/teacher/myfeedback/';
                else if(view === 'feedback') {/* 当前页 */}
            });

            // 解析URL中的学生ID /teacher/feedback/?id=xxx
            currentStudentId = getQueryParam('id');
            const stuTag = document.getElementById('currentStudent');
            const titleEl = document.getElementById('view-title');
            if(currentStudentId){
                // 优先使用点击时暂存的姓名
                let name = null;
                try{
                    const cur = JSON.parse(localStorage.getItem('currentFeedbackStudent') || 'null');
                    if(cur && String(cur.id) === String(currentStudentId)){
                        name = cur.name || null;
                    }
                }catch(e){}
                if(!name){
                    const mapStr = localStorage.getItem('studentNameIdMap') || '{}';
                    const mapObj = JSON.parse(mapStr);
                    name = Object.keys(mapObj).find(k => String(mapObj[k]) === String(currentStudentId)) || null;
                }
                if(name){
                    stuTag.textContent = `${name}（ID: ${currentStudentId}）`;
                    if(titleEl){ titleEl.textContent = `${name}的课堂反馈`; }
                }else{
                    stuTag.textContent = `ID: ${currentStudentId}`;
                    if(titleEl){ titleEl.textContent = '课堂反馈'; }
                }
            }else{
                stuTag.textContent = '未识别学生';
                if(titleEl){ titleEl.textContent = '课堂反馈'; }
            }

            // 表单绑定
            bindFeedbackForm();
            bindPhotoUpload();
            bindScheduleLoader();

            // 初始数据
            const today = new Date().toISOString().slice(0,10);
            document.getElementById('scheduleDate').value = today;
            loadSchedulesForDate();

            // 加载该学生既有反馈
            loadStudentFeedbacks();
        });

        function bindScheduleLoader(){
            document.getElementById('loadSchedulesBtn').addEventListener('click', loadSchedulesForDate);
            
            // 添加课程选择变化时的调试信息
            document.getElementById('scheduleSelect').addEventListener('change', function() {
                const selectedId = this.value;
                console.log('选择的课程ID变化:', selectedId);
            });
        }

        async function loadSchedulesForDate(){
            try {
                const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
                const teacherId = userInfo.userDTO ? userInfo.userDTO.id : null;
                const date = document.getElementById('scheduleDate').value;
                if(!teacherId || !date){ fillScheduleOptions([]); return; }

                const res = await fetch(`${API_BASE}/schedule/queryStudentSchedule?teacherId=${encodeURIComponent(teacherId)}&date=${encodeURIComponent(date)}&studentId=${encodeURIComponent(currentStudentId)}`, {
                    headers: { 'Authorization': (userInfo.token || '') }
                });
                if(!res.ok){ fillScheduleOptions([]); return; }
                const data = await res.json();
                const list = (data && data.data) ? data.data : [];
                fillScheduleOptions(list);
            } catch(err){
                fillScheduleOptions([]);
            }
        }

        async function loadStudentFeedbacks(){
            const listEl = document.getElementById('feedbackList');
            const emptyEl = document.getElementById('feedbackListEmpty');
            listEl.innerHTML = '';
            emptyEl.style.display = 'none';
            try{
                if(!currentStudentId){ emptyEl.style.display='block'; return; }
                const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
                const token = userInfo.token || '';
                // 预期接口：/feedback/listByStudent?studentId=xx
                const res = await fetch(`${API_BASE}/feedback/listByStudent?studentId=${encodeURIComponent(currentStudentId)}`, {
                    headers: { 'Authorization': token }
                });
                if(!res.ok){
                    // 后端未实现时的占位提示
                    emptyEl.textContent = '后端接口暂未实现，当前仅展示占位效果';
                    // 仅当没有任何行时才显示失败/占位
                    if (!listEl || listEl.children.length === 0) {
                        emptyEl.style.display = 'block';
                    }
                    return;
                }
                const data = await res.json();
                let list = (data && data.data) ? data.data : [];
                // 过滤掉 null/undefined 项，防止渲染时报错
                list = list.filter(Boolean);
                if(!list.length){ emptyEl.style.display='block'; return; }
                renderFeedbackList(list);
            }catch(err){
                emptyEl.textContent = '加载失败，请稍后重试';
                // 仅当没有任何行时才展示失败提示
                if (!listEl || listEl.children.length === 0) {
                    emptyEl.style.display = 'block';
                }
            }
        }

        function renderFeedbackList(list){
            const listEl = document.getElementById('feedbackList');
            const emptyEl = document.getElementById('feedbackListEmpty');
            const table = document.getElementById('feedbackTable');
            
            listEl.innerHTML = '';
            emptyEl.style.display = 'none';
            table.style.display = 'table';
            
            // 兜底再次过滤
            const safeList = (list || []).filter(Boolean);
            if(!safeList || !safeList.length){ 
                emptyEl.style.display = 'block'; 
                table.style.display = 'none';
                return; 
            }
            
            safeList.forEach((item, index) => {
                const row = document.createElement('tr');
                const time = formatDateTime(item.createTime || item.time);
                const course = item.courseName || item.courseText || '课程';
                const content = escapeHTML(item.content || '');
                const teachDate = item.teachDate || '';
                const startTime = item.startTime || '';
                const endTime = item.endTime || '';
                
                // 格式化上课时间
                let startTimeStr = '';
                let endTimeStr = '';
                
                if (startTime) {
                    if (startTime.includes('T')) {
                        startTimeStr = startTime.substring(11, 16);
                    } else if (startTime.includes(':')) {
                        startTimeStr = startTime.substring(0, 5);
                    } else {
                        startTimeStr = startTime;
                    }
                }
                
                if (endTime) {
                    if (endTime.includes('T')) {
                        endTimeStr = endTime.substring(11, 16);
                    } else if (endTime.includes(':')) {
                        endTimeStr = endTime.substring(0, 5);
                    } else {
                        endTimeStr = endTime;
                    }
                }
                
                const classTime = teachDate ? 
                    `${teachDate} ${startTimeStr} - ${endTimeStr}` : 
                    '未设置';
                
                // 截取反馈内容，超过50字符显示省略号
                const shortContent = content.length > 50 ? content.substring(0, 50) + '...' : content;
                
                row.innerHTML = `
                    <td>
                        <div class="course-name">${course}</div>
                    </td>
                    <td>
                        <div class="class-time">${classTime}</div>
                    </td>
                    <td>
                        <div class="submit-time">${time}</div>
                    </td>
                    <td>
                        <button class="btn btn-pdf" onclick="generateFeedbackPdf(${item.feedbackId || item.id || index})">
                            <i class="pdf-icon">📄</i> 生成PDF
                        </button>
                    </td>
                `;
                listEl.appendChild(row);
            });
        }

        function fillScheduleOptions(list){
            const sel = document.getElementById('scheduleSelect');
            sel.innerHTML = '<option value="">请选择当天课程（可选）</option>';
            console.log('加载课程列表:', list);
            list.forEach(item => {
                const id = item.id || item.scheduleId || '';
                const course = item.courseName || item.course || '课程';
                const clazz = item.className || item.clazz || '';
                const start = (item.startTime || '').toString().slice(0,5);
                const end = (item.endTime || '').toString().slice(0,5);
                const opt = document.createElement('option');
                opt.value = id;
                opt.textContent = `${start}-${end} ${course}${clazz ? ' · ' + clazz : ''}`;
                sel.appendChild(opt);
                console.log('添加课程选项:', { id, course, clazz, start, end });
            });
        }

        function bindPhotoUpload(){
            const input = document.getElementById('photoInput');
            const preview = document.getElementById('photoPreview');
            
            // 只处理文件选择，不立即上传
            input.addEventListener('change', function(){
                const files = Array.from(input.files || []);
                if(!files.length) return;
                if(uploadedPhotos.length + files.length > MAX_PHOTOS){
                    alert(`最多上传 ${MAX_PHOTOS} 张图片`);
                    return;
                }
                
                // 将文件转换为预览，但不立即上传
                files.forEach(file => {
                    if (file.type.startsWith('image/')) {
                        const reader = new FileReader();
                        reader.onload = function(e) {
                            uploadedPhotos.push({ 
                                file: file,  // 保存文件对象
                                url: e.target.result,  // 用于预览
                                sortNo: uploadedPhotos.length 
                            });
                            renderPhotoPreview();
                        };
                        reader.readAsDataURL(file);
                    }
                });
                input.value = '';
            });

            // 简单拖拽排序
            let dragIndex = null;
            preview.addEventListener('dragstart', (e)=>{
                const card = e.target.closest('.thumb');
                if(!card) return;
                dragIndex = Number(card.dataset.index);
            });
            preview.addEventListener('dragover', (e)=>{ e.preventDefault(); });
            preview.addEventListener('drop', (e)=>{
                e.preventDefault();
                const card = e.target.closest('.thumb');
                if(!card) return;
                const dropIndex = Number(card.dataset.index);
                if(dragIndex===null || isNaN(dropIndex)) return;
                const item = uploadedPhotos.splice(dragIndex, 1)[0];
                uploadedPhotos.splice(dropIndex, 0, item);
                renderPhotoPreview();
                dragIndex = null;
            });
        }

        async function uploadSinglePhoto(file){
            const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
            const token = userInfo.token || '';
            const fd = new FormData();
            fd.append('file', file);
            const res = await fetch(`${API_BASE}/feedback/photo/upload`, {
                method: 'POST',
                headers: { 'Authorization': token },
                body: fd
            });
            if(!res.ok){ throw new Error('上传失败'); }
            const data = await res.json();
            // 期望返回 { success:true, data:{ url: 'http://...' } }
            const url = data && data.data ? (data.data.url || data.data) : null;
            if(!url) throw new Error('返回地址为空');
            return url;
        }

        function renderPhotoPreview(){
            uploadedPhotos.forEach((p, i)=> p.sortNo = i);
            const preview = document.getElementById('photoPreview');
            preview.innerHTML = '';
            uploadedPhotos.forEach((p, i)=>{
                const div = document.createElement('div');
                div.className = 'thumb';
                div.draggable = true;
                div.dataset.index = i;
                div.style.position = 'relative';
                div.style.width = '100px';
                div.style.height = '100px';
                div.style.border = '1px solid #e5e7eb';
                div.style.borderRadius = '8px';
                div.style.overflow = 'hidden';
                div.style.marginRight = '8px';
                div.style.marginBottom = '8px';
                div.innerHTML = `
                    <img src="${p.url}" alt="photo" style="width:100%;height:100%;object-fit:cover;">
                    <button type="button" class="close-btn" style="position:absolute;right:4px;top:4px;background:rgba(0,0,0,.5);color:#fff;border:none;border-radius:50%;width:20px;height:20px;cursor:pointer;">×</button>
                `;
                div.querySelector('.close-btn').addEventListener('click', function(){
                    uploadedPhotos.splice(i,1);
                    renderPhotoPreview();
                });
                preview.appendChild(div);
            });
        }

        function bindFeedbackForm(){
            const form = document.getElementById('feedbackForm');
            const msg = document.getElementById('feedbackMsg');
            form.addEventListener('submit', async function(e){
                e.preventDefault();
                const content = document.getElementById('feedbackContent').value.trim();
                const scheduleId = document.getElementById('scheduleSelect').value || '';
                const courseText = document.getElementById('courseText').value.trim();
                if(!content){ showMsg(msg, '请填写反馈内容', 'error'); return; }

                const submitBtn = form.querySelector('button[type="submit"]');
                const original = submitBtn.textContent;
                submitBtn.disabled = true;
                submitBtn.textContent = '提交中...';
                
                try{
                    const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
                    const token = userInfo.token || '';
                    
                    // 先上传图片
                    const uploadedPhotoUrls = [];
                    if (uploadedPhotos.length > 0) {
                        submitBtn.textContent = '上传图片中...';
                        for (let i = 0; i < uploadedPhotos.length; i++) {
                            const photo = uploadedPhotos[i];
                            if (photo.file) {
                                try {
                                    const photoUrl = await uploadSinglePhoto(photo.file);
                                    uploadedPhotoUrls.push({ url: photoUrl, sortNo: photo.sortNo });
                                } catch (error) {
                                    console.error('图片上传失败:', error);
                                    // 继续上传其他图片，不中断整个流程
                                }
                            }
                        }
                    }
                    
                    // 提交反馈数据
                    submitBtn.textContent = '提交反馈中...';
                    const body = {
                        scheduleId: scheduleId ? Number(scheduleId) : null,
                        courseText: courseText || null,
                        content: content,
                        photos: uploadedPhotoUrls,
                        studentId: currentStudentId ? Number(currentStudentId) : null
                    };
                    
                    // 调试信息
                    console.log('提交反馈数据:', body);
                    console.log('选择的课程ID:', scheduleId);
                    
                    const res = await fetch(`${API_BASE}/feedback`, {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                            'Authorization': token
                        },
                        body: JSON.stringify(body)
                    });
                    
                    if(!res.ok){
                        const t = await res.text();
                        throw new Error(t || '提交失败');
                    }
                    
                    const data = await res.json();
                    if(data && (data.success === true || data.code === 200)){
                        showMsg(msg, '提交成功', 'success');

                        // 生成可分享预览数据（临时，后端未实现分享接口时使用）
                        try{
                            const sid = Date.now().toString(36);
                            const preview = {
                                time: new Date().toISOString(),
                                courseName: courseText,
                                content: content,
                                photos: uploadedPhotoUrls.map(p=>p.url)
                            };
                            sessionStorage.setItem('previewFeedback_'+sid, JSON.stringify(preview));
                            const name = (document.getElementById('currentStudent').textContent || '').replace(/（ID:.+$/,'').trim();
                            const shareUrl = `${location.origin}/share/feedback/?sid=${encodeURIComponent(sid)}&name=${encodeURIComponent(name)}`;
                            msg.innerHTML = `提交成功。可分享链接：<a href="${shareUrl}" target="_blank">${shareUrl}</a>`;
                            msg.style.display = 'block';
                        }catch(e){}

                        form.reset();
                        uploadedPhotos = []; 
                        renderPhotoPreview();
                        loadStudentFeedbacks();
                    } else {
                        throw new Error(data && (data.errorMsg || data.message) || '提交失败');
                    }
                }catch(err){
                    showMsg(msg, '提交失败：' + (err.message || err), 'error');
                }finally{
                    submitBtn.disabled = false;
                    submitBtn.textContent = original;
                }
            });
        }

        

        function formatDateTime(s){
            if(!s) return '未知时间';
            try{
                const d = new Date(s);
                return d.toLocaleDateString('zh-CN') + ' ' + d.toLocaleTimeString('zh-CN', {hour:'2-digit', minute:'2-digit'});
            }catch(e){ return s; }
        }

        function showMsg(el, msg, type='info'){
            if(!el) return;
            el.textContent = msg;
            el.className = `message ${type}`;
            el.style.display = 'block';
            setTimeout(()=>{ el.style.display='none'; }, 3000);
        }

        function escapeHTML(str){
            return (str || '').replace(/[&<>"']/g, function(m){
                return ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;','\'':'&#39;'})[m];
            });
        }

        // PDF生成功能（异步：入队 -> 轮询 -> 下载）
        async function generateFeedbackPdf(feedbackId) {
            const button = event?.target?.closest('button');
            const originalText = button ? button.innerHTML : '';
            try {
                if (button) {
                    button.disabled = true;
                    button.innerHTML = '<i class="pdf-icon">⏳</i> 生成中...';
                }

                const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
                const token = userInfo.token;
                if (!token) { alert('请先登录'); return; }

                // 1) 提交生成任务（入队 Redis Stream）
                const genRes = await fetch(`${API_BASE}/pdf/feedback/${feedbackId}/generate`, {
                    method: 'POST',
                    headers: { 'Authorization': token }
                });
                const genJson = await genRes.json();
                if (!genJson || genJson.success !== true) {
                    throw new Error(genJson?.errorMsg || '提交生成任务失败');
                }

                // 打开进度弹窗
                openPdfProgressModal();
                setPdfProgress(5, '已提交任务，等待生成...');

                // 2) 轮询状态并尝试下载
                await pollPdfReadyAndDownload(feedbackId, token);
                setPdfProgress(100, '已完成，准备下载...');
                closePdfProgressModalAfter(600);
            } catch (err) {
                console.error('PDF生成失败:', err);
                alert('PDF生成失败：' + (err.message || err));
                closePdfProgressModal();
            } finally {
                if (button) {
                    button.disabled = false;
                    button.innerHTML = originalText || '<i class="pdf-icon">📄</i> 生成PDF';
                }
            }
        }

        async function pollPdfReadyAndDownload(feedbackId, token) {
            const maxAttempts = 60; // 最多90秒
            const intervalMs = 1500;
            for (let i = 0; i < maxAttempts; i++) {
                const percent = Math.min(95, Math.round(((i + 1) / maxAttempts) * 95));
                setPdfProgress(percent, '正在生成PDF，请稍候...');
                const res = await fetch(`${API_BASE}/pdf/feedback/${feedbackId}/download`, {
                    method: 'GET',
                    headers: { 'Authorization': token }
                });
                if (res.ok) {
                    setPdfProgress(98, '生成完成，开始下载...');
                    const blob = await res.blob();
                    const url = window.URL.createObjectURL(blob);
                    const a = document.createElement('a');
                    a.href = url;
                    a.download = `课后反馈_${feedbackId}.pdf`;
                    document.body.appendChild(a);
                    a.click();
                    window.URL.revokeObjectURL(url);
                    a.remove();
                    return;
                }
                if (res.status !== 202) {
                    throw new Error('下载失败: ' + res.status);
                }
                await new Promise(r => setTimeout(r, intervalMs));
            }
            throw new Error('生成超时，请稍后重试');
        }

        async function downloadPdf(feedbackId, token) {
            const res = await fetch(`${API_BASE}/pdf/feedback/${feedbackId}/download`, {
                method: 'GET',
                headers: { 'Authorization': token }
            });
            if (!res.ok) throw new Error('下载失败: ' + res.status);
            setPdfProgress(98, '生成完成，开始下载...');
            const blob = await res.blob();
            const url = window.URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = `课后反馈_${feedbackId}.pdf`;
            document.body.appendChild(a);
            a.click();
            window.URL.revokeObjectURL(url);
            a.remove();
        }

        // ========== PDF 进度弹窗 ==========
        function openPdfProgressModal(){
            let modal = document.getElementById('pdfProgressModal');
            if(!modal){
                modal = document.createElement('div');
                modal.id = 'pdfProgressModal';
                modal.innerHTML = `
<div class="pdf-modal-backdrop"></div>
<div class="pdf-modal">
  <div class="pdf-modal-title">正在生成PDF</div>
  <div class="pdf-progress-bar"><div class="pdf-progress-fill" style="width:5%"></div></div>
  <div class="pdf-progress-text">已提交任务，等待生成...</div>
  <button class="pdf-modal-cancel" onclick="closePdfProgressModal()">关闭</button>
</div>`;
                document.body.appendChild(modal);
            }
            modal.style.display = 'block';
        }
        function setPdfProgress(percent, text){
            const fill = document.querySelector('#pdfProgressModal .pdf-progress-fill');
            const t = document.querySelector('#pdfProgressModal .pdf-progress-text');
            if(fill){ fill.style.width = Math.max(5, Math.min(100, percent)) + '%'; }
            if(t && text){ t.textContent = text; }
        }
        function closePdfProgressModal(){
            const modal = document.getElementById('pdfProgressModal');
            if(modal){ modal.style.display = 'none'; }
        }
        function closePdfProgressModalAfter(ms){ setTimeout(closePdfProgressModal, ms || 500); }
    </script>
    <style>
        #pdfProgressModal{display:none;position:fixed;inset:0;z-index:9999}
        #pdfProgressModal .pdf-modal-backdrop{position:absolute;inset:0;background:rgba(0,0,0,.35)}
        #pdfProgressModal .pdf-modal{position:relative;margin:12% auto 0;max-width:420px;background:#fff;border-radius:10px;box-shadow:0 10px 30px rgba(0,0,0,.2);padding:20px}
        .pdf-modal-title{font-weight:600;font-size:16px;margin-bottom:12px}
        .pdf-progress-bar{width:100%;height:10px;border-radius:6px;background:#f0f2f5;overflow:hidden}
        .pdf-progress-fill{height:100%;width:5%;background:linear-gradient(90deg,#4e8cff,#6bc1ff)}
        .pdf-progress-text{margin-top:8px;font-size:12px;color:#666}
        .pdf-modal-cancel{margin-top:12px;background:#f5f5f5;border:1px solid #e5e5e5;border-radius:6px;padding:6px 10px;cursor:pointer}
        .pdf-modal-cancel:hover{background:#eee}
    </style>
</body>
</html>


